----------------------------------
-- 战斗逻辑
-- combatBegin 				开始战斗
-- createCombatFakeHuman 	根据uuid创角战斗human

-- fight 					战斗开始前各模块自己的判断函数，成功调用CombatLogic.combatBegin()
-- onFightBegin 			战斗帧计算前的属性重新计算
-- onFightEnd 				战斗结束回调，发送GC_COMBAT_FINISH前
-- checkCombatPos 			可否打开上阵界面（默认可打开）

-- 获取双方上阵对象信息
-- getCombatMonsterOutID 	获取上阵怪物组id
-- getCombatTarget 			获取上阵human信息
-- getCombatObjList 		获取上阵队伍列表
----------------------------------

local CombatExcel = require("excel.combat")
local MonsterExcel = require("excel.monster")
local HeroExcel = require("excel.hero")
local SkillExcel = require("excel.skill")
local BufferExcel = require("excel.buffer")
local Config = require("Config")
local Util = require("common.Util")
local Lang = require("common.Lang")
local Log = require("common.Log")
local Msg = require("core.Msg")
local ObjHuman = require("core.ObjHuman")
local Grid = require("bag.Grid")
local Broadcast = require("broadcast.Broadcast")
local BeSkill = require("combat.BeSkill")
local CombatObj = require("combat.CombatObj")
local CombatImpl = require("combat.CombatImpl")
local CombatDefine = require("combat.CombatDefine")
local CombatPosLogic = require("combat.CombatPosLogic")
local CombatPetCalc = require("combat.CombatPetCalc")
local MoshouLogic = require("moshou.MoshouLogic")
local RoleDBLogic = require("role.RoleDBLogic")
local RoleAttr = require("role.RoleAttr")
local RoleDefine = require("role.RoleDefine")
local RoleLogic = require("role.RoleLogic")
local HeroGrid = require("hero.HeroGrid")
local DrillLogic = require("drill.DrillLogic")
local SkinLogic = require("skin.SkinLogic")
local BeginStory = require("scene.BeginStory")
local Skill = require("combat.Skill")
local CombatPosExcel = require("excel.combatPos")
local ValleyLogic = require("valley.ValleyLogic")
local LianyuLogic = require("lianyu.LianyuLogic")
local TheStarsLogic = require("theStars.TheStarsLogic")
local HeroDefine = require("hero.HeroDefine")
local JibanLogic = require("combat.JibanLogic")
local EquipLogic = require("equip.EquipLogic")
local ItemDefine = require("bag.ItemDefine")
local HeroLogic = require("hero.HeroLogic")
local XingYaoGongMing = require("xingYaoMen.XingYaoGongMing")
local MoshouLogic = require("moshou.MoshouLogic")
local LostTempleCombatLogic = require("lostTemple.lostTempleCombatLogic")

FieldsCombat = {
	lv = 1, name = 1, head = 1, headFrame = 1, unionUuid = 1, lastLogoutTime = 1, 
	combatHero = 1, heroBag = 1, moshou = 1, technology=1, chengjiu=1,
}

COMBAT_CACHE = COMBAT_CACHE or {}


------------------------------------------- 模块相关 start -------------------------------------------------

-- 初始
COMBATTYPE_2_MODULE = COMBATTYPE_2_MODULE or {}
function init()
	for combatType, cf in pairs(CombatExcel.combat) do
		if cf.moduleFn ~= "" then
			local moduleFn = load("return require(\""..cf.moduleFn.."\")")()	
			COMBATTYPE_2_MODULE[combatType] = moduleFn
		end
	end
end

-- 获取模块
function getModule(combatType)
	if not combatType then return end
	return COMBATTYPE_2_MODULE[combatType]
end

-- 获取攻/守方怪物组id
local function getCombatMonsterOutID(human, side, combatType, args)
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.getCombatMonsterOutID then
		return moduleFn.getCombatMonsterOutID(human, side, args, combatType)
	end
end


-- 获取攻/守方怪物组id
function getMapID(human, combatType, param)
	local moduleFn = getModule(combatType)
    local mapID = nil
	if moduleFn and moduleFn.getMapID then
		mapID = moduleFn.getMapID(human, param)
	end

    if not mapID then
       local config = CombatExcel.combat[combatType]
       mapID = config.mapID == 0 and 1001 or config.mapID
    end

    return mapID
end


-- 获取是否跳过
function getQuick(human, combatType)
    local config = CombatExcel.combat[combatType]
	local moduleFn = getModule(combatType)
    local quick = nil
	if moduleFn and moduleFn.getQuick then
	   quick = moduleFn.getQuick(human, combatType)
	end

    if quick ~= nil and quick >= 0 then
       return quick
    end

    if human.db.combatQuick[combatType] and human.db.combatQuick[combatType] ~= 0 then
       if config.isQuick == 0 then
          human.db.combatQuick[combatType] = 0
       end
    end

    return config.isQuick
end

-- 获取攻/守方对象
local function getCombatTarget(human, side, combatType, args)
	local moduleFn = getModule(combatType)
	if moduleFn  and moduleFn.getCombatTarget then
		return moduleFn.getCombatTarget(human, side, args, combatType)
	end
end

--更新上阵信息回调
function onUpdatePos(human,combatType)
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.onUpdatePos then
		return moduleFn.onUpdatePos(human)
	end
end

-- 获取攻/守方上阵信息
function getCombatObjList(human, side, combatType, args)
	if type(args) == "table" then
		if side == CombatDefine.ATTACK_SIDE then
			if args.attacker then
				return args.attacker, args.atkHelp, args.atkRBase, args.atkFormation,args.atkJiban
			end
		else
			if args.defender then
				return args.defender, args.defHelp, args.defRBase, args.defFormation,args.defJiban
			end
		end
	end
	local moduleFn = getModule(combatType)
	if not moduleFn then return end

	if moduleFn.getCombatObjList then
		local objList, helpList, rolebase, formation,jiban = moduleFn.getCombatObjList(human, side, args, combatType)
		if objList then 
			return objList, helpList, rolebase ,formation,jiban
		end
	end

	local monsterOutID, zhandouli = getCombatMonsterOutID(human, side, combatType, args)
	if monsterOutID then
		local objList, helpList, rolebase, formation, jiban = getMonsterObjList(monsterOutID, nil, args)
		if zhandouli then 
			rolebase.zhandouli = zhandouli
		end
		return objList, helpList, rolebase,formation,jiban
	end

    if moduleFn.getHumanObjList then
	   return moduleFn.getHumanObjList(human, combatType)
	end

	local target = getCombatTarget(human, side, combatType, args)
	if target then
		return getHumanObjList(target, combatType)
	end
	if side == CombatDefine.ATTACK_SIDE then  -- 没有模块没有特写，攻防默认取自身上阵信息
		return getHumanObjList(human, combatType)
	end
end

--获取战斗最大回合数
local function getMaxRound(human,combatType)
	local config = CombatExcel.combat[combatType]
	if config and config.maxRound and config.maxRound > 0 then
		return config.maxRound
	end
	return CombatDefine.COMBAT_ROUND_MAX
end

--获取战斗模式
local function getFightMode(combatType)
	local config = CombatExcel.combat[combatType]
	if config.fightMode[1] == CombatDefine.FIGHT_MODE1 or config.fightMode[1] == CombatDefine.FIGHT_MODE3 then
		return config.fightMode
	end
end

-- 尝试战斗
function tryCombatBegin(human, combatType, param)
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.fight then 
		local args = Util.split(param, "|")
		return moduleFn.fight(human, args, combatType)
	end
end

-- 可否打开上阵界面
function checkCombatPos(human, combatType, args)
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.checkCombatPos then 
		return moduleFn.checkCombatPos(human, args, combatType)
	end
	return true		
end


-- 战斗结束回调 在发送GC_COMBAT_FINISH前
local function onFightEnd(human, result, combatType, cbParam, combatInfo, isSaodang,param)
	local moduleFn = getModule(combatType)
	if not moduleFn then return end
	if not moduleFn.onFightEnd then return end

	moduleFn.onFightEnd(human, result, combatType, cbParam, combatInfo, isSaodang,param)
end

-- 战斗名称
function getCombatName(human, combatType, args)
	local moduleFn = getModule(combatType)
	if not moduleFn then return "" end
	if not moduleFn.getCombatName then return "" end

	return moduleFn.getCombatName(human, args, combatType) or ""
end

------------------------------------------- 模块相关 end -------------------------------------------------

-- 判断阵容是不是为空
function isCombatHeroEmpty(combatHero)
	if not combatHero then return true end

	local isEmpty = true 
	for _, heroUuid in pairs(combatHero) do
		if heroUuid and heroUuid ~= "" and heroUuid ~= "0" then
			isEmpty = nil
			break
		end
	end
	return isEmpty
end

-- 根据阵营和下标获取对战位置
function getPos(side, index)
	if side == CombatDefine.ATTACK_SIDE then
		return index
	elseif side == CombatDefine.DEFEND_SIDE then
		return index + CombatDefine.COMBAT_HERO_CNT
	else
		assert()
	end
end

function getIndexByPos(side, pos)
	if side == CombatDefine.ATTACK_SIDE then
		return pos
	elseif side == CombatDefine.DEFEND_SIDE then
		return pos - CombatDefine.COMBAT_HERO_CNT
	end	
end

-- 获取对象配置
function getConfigByObj(obj)
	if not obj then return end

	local id = obj.id or obj.heroID
	if obj.type == CombatDefine.COMBAT_OBJ_TYPE1 then
		return HeroExcel.hero[id]
	end
	return MonsterExcel.monster[id]
end

-- 创建战斗Human
function createCombatFakeHuman(uuid)
	if not uuid then return end
	local db = RoleDBLogic.getDb(uuid, FieldsCombat)
	if not db then return end

	local fakeHuman = {}
	fakeHuman.db = db
	return fakeHuman	
end

-- 创建初始角色信息
function createRoleBaseDefault()
	local rolebase = {}
	RoleLogic.makeRoleBase(nil, rolebase, nil, true)
	rolebase.name = Lang.COMBAT_DEFEND_NAME
	return rolebase
end

-- 根据怪物信息创建角色信息
function createRoleBaseByMOutID(monsterOutID, zhandouli, args)
	local _, monsterConfig, monsterLv = getMonsterIDByOutID(monsterOutID, args)
	local rolebase = createRoleBaseDefault()
	if monsterConfig then
		rolebase.name = monsterConfig.name 
		rolebase.lv = monsterLv or 1
		rolebase.head = monsterConfig.head
		rolebase.zhandouli = zhandouli or 0
	end
	return rolebase
end

-- 创建角色信息
function createRoleBaseByDB(db, zhandouli)
	local rolebase = {}
	RoleLogic.makeRoleBase(db, rolebase, nil, true)	
	rolebase.zhandouli = zhandouli or 0
	return rolebase
end

-- 根据怪物表信息创建临时对象
function createMonsterObj(id, lv, attrID, index, objType, monsterOutConfig, attrInput)
	local monsterConfig = MonsterExcel.monster[id]
	if not monsterConfig then return end			

	local obj = {}
	obj.type = objType or CombatDefine.COMBAT_OBJ_TYPE2
	obj.bagIndex = index
	obj.id = id
	obj.lv = lv			
	obj.job = monsterConfig.job
	obj.quality = 6
	obj.star = monsterConfig.star
	obj.head = monsterConfig.head
	obj.body = monsterConfig.body
    obj.camp = monsterConfig.camp
    obj.sex = monsterConfig.sex
	BeSkill.initBeSkill(obj)
	BeSkill.setBeSkill(obj, monsterConfig, nil, monsterOutConfig)

	Skill.setSkill(obj, monsterConfig)		
	obj.attrs = RoleAttr.calcMonsterConf(id, attrID, attrInput)
	return obj
end

-- 根据英雄ID信息创建临时对象
function createHeroObjByID(heroId, lv, star, quality, index)
	local heroConfig = HeroExcel.hero[heroId]
	if not heroConfig then return end
	
	local obj = {}
	obj.type = CombatDefine.COMBAT_OBJ_TYPE1
	obj.bagIndex = index
	obj.id = heroId
	obj.lv = lv
	obj.job = heroConfig.job			
	obj.quality = quality and quality or HeroGrid.getMaxQuality(star)
	obj.star = star
    obj.body = heroConfig.body
    obj.head = heroConfig.head

    local attrConfig =  HeroDefine.getAttrConfig(obj.id, obj.star)
    BeSkill.initBeSkill(obj)
	Skill.setSkill(obj, attrConfig)	
	BeSkill.setBeSkill(obj, attrConfig)
	obj.attrs = Util.copyTable(RoleAttr.calcHeroGrid(obj, index))
    obj.zhandouli = obj.attrs[RoleDefine.ZHANDOULI]
	return obj
end

-- 根据英雄表信息创建临时对象
function createHeroObj(human, heroId, lv, star, index)
	local heroConfig = HeroExcel.hero[heroId]
	if not heroConfig then return end
	
	local obj = {}
	obj.type = CombatDefine.COMBAT_OBJ_TYPE1
	obj.bagIndex = index
	obj.id = heroId
	obj.lv = lv
	obj.job = heroConfig.job			
	obj.quality = HeroGrid.getMaxQuality(star)
	obj.star = star
    obj.body = heroConfig.body
    obj.head = heroConfig.head

    local attrConfig =  HeroDefine.getAttrConfig(obj.id, obj.star)
    BeSkill.initBeSkill(obj)
    if not SkinLogic.setSkill(human,index,attrConfig,obj) then
		Skill.setSkill(obj, attrConfig)	
		BeSkill.setBeSkill(obj, attrConfig)
	end	
	obj.attrs = Util.copyTable(RoleAttr.calcHeroGrid(obj, index, human))
    obj.zhandouli = obj.attrs[RoleDefine.ZHANDOULI]
	return obj
end

function createHeroObjByHeroGrid(human, heroGrid)
	local heroConfig = HeroExcel.hero[heroGrid.id]
	if not heroConfig then return end
			
	local obj = {}
	obj.type = CombatDefine.COMBAT_OBJ_TYPE1
	obj.bagIndex = heroGrid.bagIndex
    obj.uuid = heroGrid.uuid
	obj.id = heroGrid.id
	obj.lv = heroGrid.lv
	obj.job = heroConfig.job			
	obj.quality = heroGrid.quality
	obj.star = heroGrid.star  -- dxzeng 取db star
    obj.fuwen = Util.copyTable(heroGrid.fuwen)
    obj.bingshu = Util.copyTable(heroGrid.bingshu)
    obj.body, obj.head = SkinLogic.getBody(human, obj.bagIndex)
    obj.camp = heroConfig.camp
    obj.isGongMing = XingYaoGongMing.isGongMing(human, heroGrid.bagIndex)
    obj.sex = heroConfig.sex
    local attrConfig =  HeroDefine.getAttrConfig(obj.id, obj.star)
    BeSkill.initBeSkill(obj)
    if not SkinLogic.setSkill(human,obj.bagIndex,attrConfig,obj) then
		Skill.setSkill(obj, attrConfig)	
		BeSkill.setBeSkill(obj, attrConfig)
	end
    if heroGrid.isLostTemple then
        obj.attrs = Util.copyTable(LostTempleCombatLogic.getHeroAttrs(human, heroGrid.bagIndex))
    else 
        obj.attrs = Util.copyTable(ObjHuman.getHeroAttrs(human, obj.bagIndex))
    end
    obj.zhandouli = obj.attrs[RoleDefine.ZHANDOULI]
	return obj
end

-- 根据英雄背包创建临时对象
function createHumanObj(human, uuid)
	if not uuid then return end
	local heroGrid = HeroLogic.getHeroGridByUuid(human, uuid)
	if not heroGrid or type(heroGrid) ~= "table" then return end

	return createHeroObjByHeroGrid(human, heroGrid)
end

-- 根据怪物组id获取出战英雄列表
function getMonsterObjList(monsterOutID, objType, args)
	if not monsterOutID then return end
	local monsterOutConfig = MonsterExcel.monsterOut[monsterOutID]
	if not monsterOutConfig then return end

    local formation = monsterOutConfig.formation
    if formation == 0 then
       formation = 1
    end

    local mationConfig = CombatPosExcel.formation[formation] 
    local posList = mationConfig.pos
    if not posList then return end

    local objList = nil
    local zhandouli = 0
	for i, member in ipairs(monsterOutConfig.member) do
		local monsterID = member[1]		
		local monsterConfig = MonsterExcel.monster[monsterID]			
		if posList[i] then
			local monsterLv = member[2]
			local attrID = monsterOutConfig.attrID[i]	
			local attrInput = nil
						
			if monsterOutConfig.combatType == CombatDefine.COMBAT_TYPE5 then
				if args then
					local throneID = args[1]
					local evolveCnt = args[2]
					monsterLv, attrInput = TheStarsLogic.getMonsterLvAttr(i, throneID, evolveCnt)	
				end
			end
			
			local obj = createMonsterObj(monsterID, monsterLv, attrID, i, objType, monsterOutConfig.beSkill[i], attrInput)
			if obj then
				zhandouli = zhandouli + obj.attrs[RoleDefine.ZHANDOULI]
				objList = objList or {}

                local pos = posList[i]

                if monsterOutConfig.combatType == CombatDefine.COMBAT_TYPE10 then
                    pos = 5
                end
				objList[pos] = obj
			end
		end
	end
	if not objList then
		assert(nil, "getMonsterObjList is nil, monsterOutID = " .. monsterOutID)
	end
	local rolebase = createRoleBaseByMOutID(monsterOutID, zhandouli, args)
	local jiban = nil -- todo 怪物的羁绊
	return objList, nil, rolebase,formation,jiban
end


-- 获取human出战英雄列表 objList, helpList, rolebase, formation
function getHumanObjList(human, combatType)
	local teamType = CombatPosLogic.getTeamType(combatType)
	if not teamType then return end

	local combatHero, _, formation,combatHeroDB = CombatPosLogic.getCombatHeros(human, combatType)
	if not combatHero then return end
    if formation == 0 then
       formation = 1
    end
    local jiban = JibanLogic.getJibanHero(human,combatHeroDB)
    local objList = {}
    local zhandouli = 0
	for i = 1, CombatDefine.COMBAT_HERO_CNT do
		local uuid = combatHero[i]
		local obj = createHumanObj(human, uuid)
		if obj then
			zhandouli = zhandouli + obj.attrs[RoleDefine.ZHANDOULI]
			objList[i] = obj
		end
	end
	local helpList = {}
	local pet = MoshouLogic.createCombatMoshow(human,combatType)
	helpList[CombatDefine.HELP_TYPE1] = pet
	local rolebase = createRoleBaseByDB(human.db, zhandouli)
	return objList, helpList, rolebase,formation,jiban
end



---------------------------------------------- msg -----------------------------------------------
-- 封装对战英雄信息
local function fontCombatHeroNet(net, obj)
	local config = getConfigByObj(obj)
	if not config then return end

	net.pos = obj.pos
	net.heroID = obj.id
	net.star = obj.star
	net.lv = obj.lv or 0
	net.body = obj.body or config.body
	net.hpMax = CombatObj.getHpMax(obj)
	net.hpNow = obj.initHp
	net.icon = config.head
	net.width = config.width
	net.height = config.height
	net.camp = config.camp
	net.name = config.name
	net.posX = CombatDefine.DEFAULT_POS_SIZE[obj.pos][1]
	net.posY = CombatDefine.DEFAULT_POS_SIZE[obj.pos][2]
	net.size = CombatDefine.DEFAULT_POS_SIZE[obj.pos][3]
	net.isBoss = 0
    net.isGongMing = obj.isGongMing or 0
    net.grade = config.grade or 0	
	local len = 0
	for _,v in pairs(obj.skillList) do
		if SkillExcel.skill[v[1]].contentType == 3 then
			len = len + 1
			net.sayList[len] = v[1]
		end
	end

	for _,v in pairs(obj.beSkillList) do
		for _,v1 in ipairs(v) do
			if SkillExcel.skill[v1[1]].contentType == 3 then
				len = len + 1
				net.sayList[len] = v1[1]
			end
		end
	end
	net.sayList[0] = len

	return true
end

-- 封装辅助对象
function fontCombatHelpNet(net, help)
	net.pos = help.pos
	net.id = help.id or 0
	net.head = help.head or 0
	net.body = help.body or 0

    net.skill[0] = 0 
    local skillConfig = SkillExcel.skill[help.icon]
    if skillConfig then
       net.skill[1].id = help.icon
       net.skill[1].icon = skillConfig.icon
       net.skill[1].name = skillConfig.name
       net.skill[1].desc = skillConfig.desc
       net.skill[1].lv = help.skillLV or 0
       net.skill[1].index = 0
       net.skill[1].state = 2
       net.skill[0] = 1
    end
    
	net.args[0] = 0
	if help.clientArgs then
		local len = 0
		for k,v in ipairs(help.clientArgs) do
			len = len + 1
			net.args[len] = v
		end
		net.args[0] = len
	end
	return true
end

-- 封装战斗技能信息
function fontCombatSkillNet(skillNet, skillID)
	local skillConfig = SkillExcel.skill[skillID]
	if not skillConfig then return end
	skillNet.skillID = skillID
	skillNet.fireType = skillConfig.fireType
	skillNet.readyAction = skillConfig.readyAction
	skillNet.attackPart = skillConfig.attackPart
	skillNet.attackAction = skillConfig.attackAction
	skillNet.readyEffect = skillConfig.readyEffect
	skillNet.attackEffect = skillConfig.attackEffect
	skillNet.hitEffect = skillConfig.hitEffect
	skillNet.flyEffect = skillConfig.flyEffect
	skillNet.flyCoords = skillConfig.flyCoords
	skillNet.hitBack = skillConfig.hitBack
	skillNet.effectTime = skillConfig.effectTime
	skillNet.contentType = skillConfig.contentType
	skillNet.content = skillConfig.content
	skillNet.movieEffect = skillConfig.movieEffect
	skillNet.quake = skillConfig.quake
	skillNet.flySound = skillConfig.flySound
	skillNet.fireSound = skillConfig.fireSound
	skillNet.hitSound = skillConfig.hitSound
    skillNet.cvSound = skillConfig.cvSound
    skillNet.screenMask = skillConfig.screenMask
    skillNet.screenCam = skillConfig.screenCam
    skillNet.largeEffect = skillConfig.largeEffect
    skillNet.lie = skillConfig.lie
    skillNet.cuoZhen = skillConfig.cuoZhen
    skillNet.skillDelay = skillConfig.skillDelay
    skillNet.isFraming = skillConfig.isFraming
	return true
end

-- 封装技能cmd
local function fontSkillCmd(cmdNet, cmd)
	local cmdConfig = SkillExcel.cmd[cmd]
	if not cmdConfig then return end

	cmdNet.cmd = cmd
	cmdNet.hitEffect = cmdConfig.hitEffect
	cmdNet.hitSound = cmdConfig.hitSound
	return true
end

-- 封装buff
local function fontBufferNet(bufferNet, bufferID)
	local bufferConf = BufferExcel.buffer[bufferID]
	if not bufferConf then return end

	bufferNet.bufferID = bufferID
	bufferNet.name = bufferConf.name
	bufferNet.desc = bufferConf.desc
	bufferNet.icon = bufferConf.icon
	bufferNet.keepEffect = bufferConf.keepEffect
	bufferNet.hitEffect = bufferConf.hitEffect
	bufferNet.validateEffect = bufferConf.validateEffect
	bufferNet.dieEffect = bufferConf.dieEffect
	bufferNet.cmd = bufferConf.cmd
	bufferNet.hitSound = bufferConf.hitSound
	bufferNet.holdOnDie = bufferConf.holdOnDie
	bufferNet.effectOffset = bufferConf.effectOffset
    bufferNet.canAppend = bufferConf.canAppend
    bufferNet.appendCnt = bufferConf.appendCnt
	return true
end

-- 封装阵法属性
local function fontCombatPosAttr(attrNets, attrs)
	attrNets[0] = 0
	if type(attrs) ~= "table" then return end
	for key, value in pairs(attrs) do
		attrNets[0] = attrNets[0] + 1
		local attrNet = attrNets[attrNets[0]]
		attrNet.key = key
		attrNet.value = value
	end	
end

-- 发送战斗开始
local function sendCombatBegin(human, combatInfo, isLogin)
	combatInfo = combatInfo or human.combat 
	if not combatInfo then return end

	local msgRet = Msg.gc.GC_COMBAT_BEGIN
	msgRet.isLogin = isLogin == true and 1 or 0
	msgRet.combatType = combatInfo.type
	local keepTime = os.time() - combatInfo.time
    if combatInfo.passTime < keepTime then
       combatInfo.passTime = keepTime
    end
    msgRet.keepTime = combatInfo.passTime
	if combatInfo.isVideo then -- 录像回放
		msgRet.keepTime = 0
	end

    local isCanQuick = getQuick(human, combatInfo.type)
    msgRet.isQuick = isCanQuick or 0
    msgRet.isVideo = combatInfo.isVideo == true and 1 or 0
	msgRet.mapID = combatInfo.mapID
	msgRet.speed = human.db and human.db.combatSpeed or 1
	msgRet.atkName = combatInfo.attacker and combatInfo.attacker.name or ""
	msgRet.defName = combatInfo.defender and combatInfo.defender.name or ""
    msgRet.atkUuid = combatInfo.attacker and combatInfo.attacker.uuid or ""
	msgRet.defUuid = combatInfo.defender and combatInfo.defender.uuid or ""
	msgRet.maxRound = combatInfo.maxRound
 
	msgRet.petCD = CombatDefine.PET_CD
	msgRet.heros[0] = 0
    msgRet.atkFormation = combatInfo.atkFormation
    msgRet.defFormation = combatInfo.defFormation
    msgRet.backup[0] = 2
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		local obj = combatInfo.objList[i]
		if fontCombatHeroNet(msgRet.heros[msgRet.heros[0] + 1], obj) then
			msgRet.heros[0] = msgRet.heros[0] + 1
		end
		if i % CombatDefine.COMBAT_HERO_CNT == 0 then
			msgRet.backup[i/CombatDefine.COMBAT_HERO_CNT] = obj and obj.backupPos or 0
		end
	end
	msgRet.helps[0] = 0

	for _, help in pairs(combatInfo.helpList) do
		if fontCombatHelpNet(msgRet.helps[msgRet.helps[0] + 1], help) then
			msgRet.helps[0] = msgRet.helps[0] + 1
		end
	end	
	msgRet.skillList[0] = 0
	for skillID in pairs(combatInfo.skillUseList) do
		if fontCombatSkillNet(msgRet.skillList[msgRet.skillList[0] + 1], skillID) then
			msgRet.skillList[0] = msgRet.skillList[0] + 1
		end	
	end	
	msgRet.cmdList[0] = 0
	if combatInfo.cmdUseList then
		for cmd in pairs(combatInfo.cmdUseList) do
			if fontSkillCmd(msgRet.cmdList[msgRet.cmdList[0] + 1], cmd) then
				msgRet.cmdList[0] = msgRet.cmdList[0] + 1
			end
		end	
	end
	msgRet.bufferList[0] = 0
	for bufferID in pairs(combatInfo.bufferUseList) do
		if fontBufferNet(msgRet.bufferList[msgRet.bufferList[0] + 1], bufferID) then
			msgRet.bufferList[0] = msgRet.bufferList[0] + 1
		end
	end	
	fontCombatPosAttr(msgRet.attrsAtk, combatInfo.posAttr[1])
	fontCombatPosAttr(msgRet.attrsDef, combatInfo.posAttr[2])	
	msgRet.atkJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.ATTACK_SIDE])
	msgRet.defJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.DEFEND_SIDE]) 
	Msg.send(msgRet, human.fd)
end

-- 封装战斗帧技能信息
local function fontFrameSkillNet(skillNet, skillData)
	skillNet.attackPos = skillData.attackPos
	skillNet.skillID = skillData.skillID

	skillNet.hitList[0] = 0
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		local skillHitData = skillData.hitList[i] 
		if skillHitData then
			skillNet.hitList[0] = skillNet.hitList[0] + 1
			local skillHitNet = skillNet.hitList[skillNet.hitList[0]]
			skillHitNet.pos = i
			skillHitNet.flag = skillHitData.flag
			skillHitNet.cmd = 0
			skillHitNet.hpNow = skillHitData.hpNow
			local frameAttrs = skillHitData.attrs
			skillHitNet.attrs[0] = frameAttrs and #frameAttrs or 0	
			for j = 1, #frameAttrs do
				skillHitNet.attrs[j].key = frameAttrs[j][1]
				skillHitNet.attrs[j].value = frameAttrs[j][2]
			end							
		end
	end

	skillNet.helpList[0] = 0
	--for i = CombatDefine.PET_POS_ATTACK, CombatDefine.PET_POS_DEFEND do
	--	local helpHitData = skillData.helpList and skillData.helpList[i]
	--	if helpHitData then
	--		skillNet.helpList[0] = skillNet.helpList[0] + 1
	--		local helpHitNet = skillNet.helpList[skillNet.helpList[0]]
	--		helpHitNet.pos = i
	--	end
	--end

	skillNet.extraList[0] = 0
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		local extraHitData = skillData.extraList[i]
		if extraHitData then
			skillNet.extraList[0] = skillNet.extraList[0] + 1			 
			local extraHitNet = skillNet.extraList[skillNet.extraList[0]]
			extraHitNet.pos = i
			extraHitNet.flag = extraHitData.flag
			extraHitNet.cmd = extraHitData.cmd
			extraHitNet.hpNow = extraHitData.hpNow
			local frameAttrs = extraHitData.attrs
			local attrsLen = math.min(frameAttrs and #frameAttrs or 0, #extraHitNet.attrs)
			extraHitNet.attrs[0] = attrsLen
			for j = 1, attrsLen do
				extraHitNet.attrs[j].key = frameAttrs[j][1]
				extraHitNet.attrs[j].value = frameAttrs[j][2]
			end		
		end
	end
end

-- 封装战斗帧buff信息
local function fontFrameBuff(net, buffers)
	net[0] = #buffers
	for i = 1, net[0] do
		local bufferNet = net[i]
		local bufferData = buffers[i]
		bufferNet.bufferID = bufferData.id
		bufferNet.op = bufferData.op
		bufferNet.cnt = bufferData.cnt
		bufferNet.round = bufferData.round or -1
		bufferNet.hpNow = bufferData.hpNow
		bufferNet.attrs[0] = bufferData.attrs and #bufferData.attrs or 0
		for j = 1, bufferNet.attrs[0] do
			bufferNet.attrs[j].key = bufferData.attrs[j][1]
			bufferNet.attrs[j].value = bufferData.attrs[j][2]
		end
	end
end

-- 封装战斗帧
local function fontFrameNet(net, frame)
	net.round = frame.round
	net.petCD = frame.petCD
	net.skillList[0] = #frame.skillList
	for i = 1, net.skillList[0] do		
		local skillNet = net.skillList[i]
		local skillData = frame.skillList[i]
		fontFrameSkillNet(skillNet, skillData)
	end
	
	net.bufferList[0] = 0
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		local bufferData = frame.bufferList[i]
		if bufferData then
			net.bufferList[0] = net.bufferList[0] + 1
			local bufferNet = net.bufferList[net.bufferList[0]]
			bufferNet.pos = i
			fontFrameBuff(bufferNet.list, bufferData)
		end
	end

	net.sayList[0] = 0
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		if frame.sayList[i] then
			for _,saySkillID in ipairs(frame.sayList[i]) do
				net.sayList[0] = net.sayList[0] + 1
				local sayNet = net.sayList[net.sayList[0]]
				sayNet.pos = i
				sayNet.skillID = saySkillID
			end
		end
	end
	return net.skillList[0] > 0 or net.bufferList[0] > 0 or net.sayList[0] > 0
end

-- 发送战斗帧
local function sendCombatFrame(human, combatInfo, isLogin)
	combatInfo = combatInfo or human.combat 
	if not combatInfo then return end

	local result = combatInfo.result
	local msgFrame = Msg.gc.GC_COMBAT_FRAME
	msgFrame.isLogin = isLogin == true and 1 or 0
	msgFrame.isEnd = 0
	msgFrame.combatType = combatInfo.type
	msgFrame.frames[0] = 0

	local frameCnt = result.frames and #result.frames or 0	
	local len = 1
	for i = 1, frameCnt do		
		local frame = result.frames[i]
		msgFrame.frames[0] = len
		local frameNet = msgFrame.frames[msgFrame.frames[0]]
		if fontFrameNet(frameNet, frame) then
			if len >= CombatDefine.COMBAT_FRAME_MAXCNT then					
				msgFrame.isEnd = (i == frameCnt) and 1 or 0
				Msg.send(msgFrame, human.fd)
				msgFrame.frames[0] = 0
				len = 1
			else
				len = len + 1
			end
		end		
	end
	if msgFrame.isEnd == 0 or msgFrame.frames[0] > 0 then
		msgFrame.isEnd = 1
		Msg.send(msgFrame, human.fd)
	end
end

-- 发送战斗开始
function sendCombatData(human, combatInfo, isLogin)
	sendCombatBegin(human, combatInfo, isLogin)
	sendCombatFrame(human, combatInfo, isLogin)
end

-- 封装战斗统计
function fontFinishResultNet(net, obj)
	net.pos = obj.pos
	net.hurt = obj.result[1]
	net.hp = obj.result[2]
	net.beHurt = obj.result[3]
	net.bout = obj.result[4]
	net.isDie = obj.hp == 0 and 1 or 0
	net.heroID = obj.id or 0
	net.head = obj.head or 0
	net.lv = obj.lv or 0
	net.star = obj.star or 0

    local heroConfig = HeroExcel.hero[net.heroID]
    local monsterConfig = MonsterExcel.monster[net.heroID]
    if obj.type == CombatDefine.COMBAT_OBJ_TYPE1 then
       net.name = heroConfig and heroConfig.name or ""
       net.body = heroConfig and heroConfig.body.."" or ""
       net.head = heroConfig and heroConfig.head or 0
       net.grade = heroConfig and heroConfig.grade or 0
    else
       net.name = monsterConfig and monsterConfig.name or ""
       net.body = monsterConfig and monsterConfig.body.."" or ""
       net.head = monsterConfig and monsterConfig.head or 0
       net.grade = monsterConfig and monsterConfig.grade or 0
    end

	net.camp = heroConfig and heroConfig.camp or 0
end

-- 封装战斗结束魔兽信息
local function fontFinishPetNet(net, pet)
	net.pos = pet.pos
	net.side = pet.side
	net.lv = pet.lv or 0
	net.head = pet.head
end


function fontCombatFinish(dataNet, combatInfo, isQuick)
	if not combatInfo then return end
	local combatConfig = CombatExcel.combat[combatInfo.type]
	if not combatConfig then return end

	dataNet.ret = combatInfo.isWin and 1 or 2
	dataNet.type = combatInfo.type
	dataNet.param = combatInfo.endParam or "0"
	dataNet.isVideo = (combatInfo.isVideo == true) and 1 or 0
	dataNet.lookType = combatInfo.lookType or 0
	dataNet.isQuick = isQuick or 0
    dataNet.combatTime = getCombatUseTime(combatInfo)
	dataNet.double = combatInfo.double or 0
    dataNet.oldLv = combatInfo.attacker.oldLv or 0
    dataNet.panelIDs[0] = #combatConfig.panelIDs
    for i, panelID in ipairs(combatConfig.panelIDs) do
	   dataNet.panelIDs[i] = tonumber(panelID) 
	end

	if combatInfo.panelID then
		dataNet.panelIDs[0] = dataNet.panelIDs[0] + 1
		dataNet.panelIDs[dataNet.panelIDs[0]] = tonumber(combatInfo.panelID)
	end
	-- 攻守方基本信息
    RoleLogic.makeRoleBase(combatInfo.defender, dataNet.target)
    RoleLogic.makeRoleBase(combatInfo.attacker, dataNet.self)

	-- 每个出战单位的战斗统计
	dataNet.result[0] = 0
	for pos = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		local obj = combatInfo.objList and combatInfo.objList[pos]
		if obj then
			dataNet.result[0] = dataNet.result[0] + 1
			local net = dataNet.result[dataNet.result[0]]
			fontFinishResultNet(net, obj)
		end 
	end
	-- 出战魔兽
    for _,pos in ipairs(CombatDefine.SIDE2HELPPOS[0]) do
		local pet = combatInfo.helpList and combatInfo.helpList[pos]
		if pet and pet.isPet then
			dataNet.result[0] = dataNet.result[0] + 1
			local net = dataNet.result[dataNet.result[0]]
			fontFinishResultNet(net, pet)
		end
	end
	-- 奖励
	local rewardItem = combatInfo.rewardItem
    local len = 0 
	for k, v in pairs(rewardItem) do
        if not ItemDefine.isEquip(v[1]) then
           len = len + 1
		   Grid.makeItem(dataNet.items[len], v[1], v[2])
        end		
	end
    dataNet.items[0] = EquipLogic.makeEquipItem(combatInfo, dataNet.items, len)

	return true
end

-- 发送战斗结束
function sendCombatFinish(human, combatInfo)
	local msgRet = Msg.gc.GC_COMBAT_FINISH
    local dataNet = msgRet.data
    local isQuick = human.db.combatQuick[combatInfo.type] or 0
    if not fontCombatFinish(dataNet, combatInfo, isQuick) then
       return
    end
    BeginStory.handleBattleFail(human, combatInfo.isWin, combatInfo.type, combatInfo.isVideo, msgRet)	
	BeginStory.handleBattle8End(human, combatInfo.isWin, combatInfo.isVideo, combatInfo.type, msgRet)
	BeginStory.handleBattle12End(human, combatInfo.isWin, combatInfo.isVideo, combatInfo.type, msgRet)
	Msg.send(msgRet,human.fd)
end

---------------------------------------- 下面是战斗各种设置逻辑 ------------------------------------------------
local function getMonsterBossName(param)
	local monsterOutID = param.monsterOutID
    local posList = param.posList
	for i,member in ipairs(MonsterExcel.monsterOut[monsterOutID].member) do
		local monsterID = member[1]
		local monsterConfig = MonsterExcel.monster[monsterID]
		if monsterConfig and posList[monsterID] ~= nil then
			return monsterConfig.name
		end
	end
end

-- 在各种战前加成后，重新设置血量
function recalcHpFightBegin(human, combatType, cbParam)
	for i = 1,CombatDefine.COMBAT_HERO_CNT do
		local atkPos = getPos(CombatDefine.ATTACK_SIDE, i)
		local atkObj = CombatImpl.objList[atkPos]
		if atkObj then
			if atkObj.isSysAttrChange then
                CombatObj.calcAttr(atkObj)
				-- 重新计算血量
				if combatType == CombatDefine.COMBAT_TYPE9 then
					DrillLogic.calcAttrAtk(human, i, atkObj, cbParam)
				elseif combatType == CombatDefine.COMBAT_TYPE17 then
					ValleyLogic.calcAttr(CombatDefine.ATTACK_SIDE, i, atkObj, cbParam)
				elseif combatType == CombatDefine.COMBAT_TYPE8 then
					LianyuLogic.recalcHpFightBegin(human, i, atkObj)
                end
			end						
			atkObj.hp = atkObj.hp or CombatObj.getHpMax(atkObj)
			atkObj.mp = atkObj.mp or atkObj.attr[RoleDefine.INIT_MP]
			atkObj.initHp = atkObj.hp
			atkObj.initMp = atkObj.mp
		end		
		local defPos = getPos(CombatDefine.DEFEND_SIDE, i)
		local defObj = CombatImpl.objList[defPos]
		if defObj then
			if defObj.isSysAttrChange then
                CombatObj.calcAttr(defObj)
				if combatType == CombatDefine.COMBAT_TYPE9 then
					DrillLogic.calcAttrDef(human, i, defObj, cbParam)
				elseif combatType == CombatDefine.COMBAT_TYPE17 then
					ValleyLogic.calcAttr(CombatDefine.DEFEND_SIDE, i, defObj, cbParam)
				end	
			end			
			defObj.hp = defObj.hp or CombatObj.getHpMax(defObj)
			defObj.mp = defObj.mp or defObj.attr[RoleDefine.INIT_MP]
			defObj.initHp = defObj.hp
			defObj.initMp = defObj.mp
		end
	end
end

--战前加属性（包括各系统，被动技能，阵营光环）
function onFightBegin(human, combatType, cbParam, param)
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.onFightBegin then
		moduleFn.onFightBegin(human, cbParam, combatType, param)
	end
	
	BeSkill.onFightBegin(human)
	CombatPosLogic.onFightBegin(human)
	recalcHpFightBegin(human, combatType, cbParam)
end


function getHumanHeroList(human,combatType, cbParam)
	return getHumanObjList(human, combatType)
end
-- 设置英雄出战
local function setCombatInfo(formation,objList, side)
	for index = 1, CombatDefine.COMBAT_HERO_CNT do
		local obj = objList[index]
		if obj then
				-- 出战英雄站位
			if CombatPosLogic.checkPos(formation,index, side) then
				local pos = CombatDefine.SIDE2POS[side][index]
				CombatImpl.setData(pos, obj)
			end
		end
	end
end

-- 设置辅助对象出战
local function setHelp(helpList, side)
	if not helpList then return end
	for k,v in pairs(helpList) do
		if k ==  CombatDefine.HELP_TYPE1 then
			CombatPetCalc.calcGrowArgs(v)
			CombatPetCalc.clacBuffArgs(v)
		end
		CombatImpl.setHelp(side,k,v)
	end
end

-- 初始combatInfo
local function initCombatInfo(human, combatType, mapID)
	local combatInfo = {}
	combatInfo.time = os.time() 	-- 战斗时间
	combatInfo.passTime = 0 	    -- 客户端经过的时间
	combatInfo.type = combatType 	-- 战斗类型
	combatInfo.mapID = mapID  		-- 地图
	combatInfo.posAttr = {}			-- 阵法属性
	combatInfo.jiban = {}			-- 阵法属性
	combatInfo.attacker = nil  		-- 攻方基础角色信息 roleBase
	combatInfo.defender = nil 		-- 守方基础角色信息 roleBase
	combatInfo.atkJiban = nil 		-- 攻方羁绊英雄
	combatInfo.defJiban = nil 		-- 守方羁绊英雄	
	combatInfo.isWin = nil 			-- 战斗结果
	combatInfo.result = nil 		-- 战斗帧
	combatInfo.skillUseList = nil 	-- 战斗技能
	combatInfo.cmdUseList = nil 	-- 战斗技能命令
	combatInfo.bufferUseList = nil 	-- 战斗buff
	combatInfo.objList = nil  		-- 战斗英雄列表
	combatInfo.helpList = nil  		-- 战斗魔兽列表
	combatInfo.rewardItem = {} 		-- 奖励
	combatInfo.endParam = nil 		-- 额外参数
	combatInfo.isVideo = nil 		-- 是否录像	
	combatInfo.videoUuid = nil 		-- 录像uuid
	combatInfo.panelID = nil 		-- 返回界面（特殊）
	combatInfo.maxRound = nil 		-- 最大会合数
	combatInfo.fightMode = nil 		-- 战斗模式
	human.combat = combatInfo
	return combatInfo
end	

-- 为了战斗回放，某些数据需要保存
local function copyToCombatInfo(combatInfo)
	combatInfo.isWin = CombatImpl.result.isWin		
	combatInfo.result = Util.copyTable(CombatImpl.result)
	combatInfo.result.round = CombatImpl.round
	combatInfo.skillUseList = Util.copyTable(CombatImpl.skillUseList)
	combatInfo.cmdUseList = Util.copyTable(CombatImpl.cmdUseList)
	combatInfo.bufferUseList = Util.copyTable(CombatImpl.bufferUseList)
	combatInfo.objList = Util.copyTable(CombatImpl.objList)	
	combatInfo.helpList = Util.copyTable(CombatImpl.helpList)
	combatInfo.totalAtkCnt = CombatImpl.totalAtkCnt
end

function setSkillAndBeskill(human,combatInfo)
	local combatType = combatInfo.type
	local moduleFn = getModule(combatType)
	if moduleFn and moduleFn.setSkillAndBeskill then
		moduleFn.setSkillAndBeskill(human,combatInfo)
	end	
end

function combatBegin(human, mapID, param, combatType, cbParam, isSaodang)
	if COMBAT_CACHE[human.db._id] and COMBAT_CACHE[human.db._id][combatType] then
		--assert(nil, "has combat cache "..combatType .. " " .. human.db.account) -- reyes test
		human.combat = COMBAT_CACHE[human.db._id][combatType].combatInfo	
		sendCombatData(human, human.combat)
		return
	end

    -- 获得地图ID
    CombatImpl.init(combatType)
    mapID = getMapID(human, combatType, param)
	local defender, defHelp, defRBase,defFormation,defJiban = getCombatObjList(human, CombatDefine.DEFEND_SIDE, combatType, param)
	local attacker, atkHelp, atkRBase, atkFormation,atkJiban
	if defender then
		attacker, atkHelp, atkRBase, atkFormation,atkJiban = getCombatObjList(human, CombatDefine.ATTACK_SIDE, combatType, param)
	end		

	if not (attacker and next(attacker)) then
		return
	end

	if not defender then
		return
	end

    

 	local combatInfo = initCombatInfo(human, combatType, mapID)
 	combatInfo.attacker = atkRBase or {}
 	combatInfo.atkHelp = atkHelp or {}
 	combatInfo.atkFormation = atkFormation
 	combatInfo.atkJiban = atkJiban
 	combatInfo.defender = defRBase or {}
 	combatInfo.defHelp = defHelp or {}
 	combatInfo.defFormation = defFormation
	combatInfo.defJiban = defJiban
	combatInfo.posAttr[CombatDefine.ATTACK_SIDE] = CombatPosLogic.getPosAttr(attacker)
	combatInfo.posAttr[CombatDefine.DEFEND_SIDE] = CombatPosLogic.getPosAttr(defender) 
	combatInfo.jiban[CombatDefine.ATTACK_SIDE] = JibanLogic.getJiban(attacker,atkJiban)
	combatInfo.jiban[CombatDefine.DEFEND_SIDE] = JibanLogic.getJiban(defender,defJiban) 
	combatInfo.maxRound = getMaxRound(human,combatType)
	combatInfo.fightMode = getFightMode(combatType)
	--战役第五关特殊处理
	BeginStory.handleBattle5(human, combatType, defender)


	JibanLogic.setBeSkill(attacker,combatInfo.jiban[CombatDefine.ATTACK_SIDE])
	JibanLogic.setBeSkill(defender,combatInfo.jiban[CombatDefine.DEFEND_SIDE])

	
    local changePos = nil
    if combatType == CombatDefine.COMBAT_TYPE10 then
        for k,v in pairs(attacker) do
            changePos = k
            attacker[k] = nil
            attacker[5] = v
            break
        end
    end
	setCombatInfo(atkFormation, attacker, CombatDefine.ATTACK_SIDE)
	setCombatInfo(defFormation, defender, CombatDefine.DEFEND_SIDE)
	setHelp(atkHelp, CombatDefine.ATTACK_SIDE)
	setHelp(defHelp, CombatDefine.DEFEND_SIDE)
	CombatImpl.setMaxRound(combatInfo.maxRound)
	CombatImpl.setFightMode(combatInfo.fightMode)
	--需要对技能特殊处理的（比如不重置cd）
	setSkillAndBeskill(human,combatInfo)
	-- 战前属性/血量重算
    local tempParam = cbParam
    if combatType == CombatDefine.COMBAT_TYPE10 then
        tempParam = {}
        tempParam.cbParam = cbParam
        tempParam.changePos = changePos
    end
	onFightBegin(human, combatType, tempParam, param)
	makeAttrLogStr(human)

	while CombatImpl.calcFrame() do end    
    copyToCombatInfo(combatInfo)

    combatInfo.changePos = changePos

	local result = combatInfo.isWin and CombatDefine.RESULT_WIN or CombatDefine.RESULT_FAIL	
	local isQuick = human.db.combatQuick[combatType] == 1-- 是否跳过战斗
	if isSaodang or isQuick then 
		-- 扫荡/跳过战斗需要立刻出结果
		onFightEnd(human, result, combatType, cbParam, combatInfo, isSaodang,param)
		sendCombatFinish(human, combatInfo)
	else
		-- 发送战斗开始+战斗帧
		sendCombatData(human, combatInfo) 
		addCombatCache(human.db._id, combatInfo, cbParam,param)
	end 
	makeLogStr(human) -- 写战斗日志
end

function getCombatUseTime(combatInfo)
    local combatUseTime = CombatDefine.COMBAT_ATK_TIME * (combatInfo.totalAtkCnt or 0) -- 1.2秒1次行动
    combatUseTime = combatUseTime + 10  --带上出场时间
	return combatUseTime
end

function combatGm(human,mapID,atkFormation,attacker,defFormation,defender,atkPet,defPet,rewardItem)
	local combatType = 0
    local config = CombatExcel.combat[combatType]
 	local combatInfo = initCombatInfo(human, combatType, mapID)
 	combatInfo.attacker = {}
 	combatInfo.defender = {}
	combatInfo.posAttr[CombatDefine.ATTACK_SIDE] = CombatPosLogic.getPosAttr(attacker)
	combatInfo.posAttr[CombatDefine.DEFEND_SIDE] = CombatPosLogic.getPosAttr(defender) 
	combatInfo.jiban[CombatDefine.ATTACK_SIDE] = JibanLogic.getJiban(attacker)
	combatInfo.jiban[CombatDefine.DEFEND_SIDE] = JibanLogic.getJiban(defender) 		
	combatInfo.rewardItem = rewardItem or combatInfo.rewardItem
    combatInfo.atkFormation = atkFormation
 	combatInfo.defFormation = defFormation
    combatInfo.fightMode = config.fightMode

	CombatImpl.init(combatType)
	setCombatInfo(atkFormation,attacker, CombatDefine.ATTACK_SIDE)
	setCombatInfo(defFormation,defender, CombatDefine.DEFEND_SIDE)
	setHelp({atkPet}, CombatDefine.ATTACK_SIDE)
	setHelp({defPet}, CombatDefine.DEFEND_SIDE)
	combatInfo.maxRound = getMaxRound(human,combatType)
	CombatImpl.setMaxRound(combatInfo.maxRound)
    CombatImpl.setFightMode(combatInfo.fightMode)

	-- 战前属性/血量重算
	onFightBegin(human, combatType, nil, {}, {})
	makeAttrLogStr(human)

	while CombatImpl.calcFrame() do end
	copyToCombatInfo(combatInfo)

	sendCombatData(human, combatInfo)

    -- 方便调试不做缓存
	addCombatCache(human.db._id, combatInfo)

    makeLogStr(human) -- 写战斗日志
end


function combatGuide(human, atkFormation,attacker,defFormation,defender,atkPet,defPet)
    local combatType = CombatDefine.COMBAT_TYPE18
    local config = CombatExcel.combat[combatType]
 	local combatInfo = initCombatInfo(human, combatType, config.mapID)
 	combatInfo.attacker = {}
 	combatInfo.defender = {}
	combatInfo.posAttr[CombatDefine.ATTACK_SIDE] = CombatPosLogic.getPosAttr(attacker)
	combatInfo.posAttr[CombatDefine.DEFEND_SIDE] = CombatPosLogic.getPosAttr(defender) 
	combatInfo.jiban[CombatDefine.ATTACK_SIDE] = JibanLogic.getJiban(attacker)
	combatInfo.jiban[CombatDefine.DEFEND_SIDE] = JibanLogic.getJiban(defender) 		
	combatInfo.rewardItem = rewardItem or combatInfo.rewardItem
    combatInfo.atkFormation = atkFormation
 	combatInfo.defFormation = defFormation
    combatInfo.fightMode = config.fightMode

	CombatImpl.init(combatType)
	setCombatInfo(atkFormation,attacker, CombatDefine.ATTACK_SIDE)
	setCombatInfo(defFormation,defender, CombatDefine.DEFEND_SIDE)
	setHelp({atkPet}, CombatDefine.ATTACK_SIDE)
	setHelp({defPet}, CombatDefine.DEFEND_SIDE)
	combatInfo.maxRound = getMaxRound(human, combatType)
	CombatImpl.setMaxRound(combatInfo.maxRound)
    CombatImpl.setFightMode(combatInfo.fightMode)

    JibanLogic.setBeSkill(attacker,combatInfo.jiban[CombatDefine.ATTACK_SIDE])
	JibanLogic.setBeSkill(defender,combatInfo.jiban[CombatDefine.DEFEND_SIDE])

	-- 战前属性/血量重算
	onFightBegin(human, combatType, nil, {}, {})

    human.combat = combatInfo

	while CombatImpl.calcFrame() do end
	copyToCombatInfo(human.combat)
end

--
function combatVedio(human, atkFormation, attacker,defFormation,defender,atkPet,defPet, fakeHuman)
    local combatType = CombatDefine.COMBAT_TYPE22
    local config = CombatExcel.combat[combatType]
 	local combatInfo = initCombatInfo(human, combatType, config.mapID)
 	combatInfo.attacker = {}
 	combatInfo.defender = {}
	combatInfo.posAttr[CombatDefine.ATTACK_SIDE] = CombatPosLogic.getPosAttr(attacker)
	combatInfo.posAttr[CombatDefine.DEFEND_SIDE] = CombatPosLogic.getPosAttr(defender) 
	combatInfo.jiban[CombatDefine.ATTACK_SIDE] = JibanLogic.getJiban(attacker)
	combatInfo.jiban[CombatDefine.DEFEND_SIDE] = JibanLogic.getJiban(defender) 		
	combatInfo.rewardItem = rewardItem or combatInfo.rewardItem
    combatInfo.atkFormation = atkFormation
 	combatInfo.defFormation = defFormation
    combatInfo.fightMode = config.fightMode

	CombatImpl.init(combatType)
	setCombatInfo(atkFormation,attacker, CombatDefine.ATTACK_SIDE)
	setCombatInfo(defFormation,defender, CombatDefine.DEFEND_SIDE)
	setHelp({atkPet}, CombatDefine.ATTACK_SIDE)
	setHelp({defPet}, CombatDefine.DEFEND_SIDE)
	combatInfo.maxRound = getMaxRound(human, combatType)
	CombatImpl.setMaxRound(combatInfo.maxRound)
    CombatImpl.setFightMode(combatInfo.fightMode)

    JibanLogic.setBeSkill(attacker,combatInfo.jiban[CombatDefine.ATTACK_SIDE])
	JibanLogic.setBeSkill(defender,combatInfo.jiban[CombatDefine.DEFEND_SIDE])

	-- 战前属性/血量重算
	onFightBegin(human, combatType, nil, {}, {})

    human.combat = combatInfo

	while CombatImpl.calcFrame() do end
    copyToCombatInfo(human.combat)

    sendCombatData(fakeHuman, combatInfo)
end

-- 战斗录像
local VEDIO_COMBAT_INFO = VEDIO_COMBAT_INFO or { db = {} }
function combatFightVedio(human, heroID)
    local heroConfig = HeroExcel.hero[heroID]
    if not heroConfig then return end

    local combatConf = CombatExcel.gm[heroConfig.gm]
    if not combatConf then return end
    
    VEDIO_COMBAT_INFO.attacker = {}
    VEDIO_COMBAT_INFO.defender = {}
    VEDIO_COMBAT_INFO.atkFormation = combatConf.atkF
    VEDIO_COMBAT_INFO.defFormation = combatConf.defF
    local atkConf = nil
    for i = 1, CombatDefine.COMBAT_HERO_CNT do
        atkConf = nil
        for k, v in ipairs(combatConf.atk) do
           if i == v[2] then
              atkConf = v
              break
           end
        end
        if atkConf then
           VEDIO_COMBAT_INFO.attacker[i] = createHeroObj(VEDIO_COMBAT_INFO, atkConf[1], atkConf[3], atkConf[4], i)
        end 

        atkConf = nil
        for k, v in ipairs(combatConf.def) do
           if i == v[2] then
              atkConf = v
              break
           end
        end
        if atkConf then
           VEDIO_COMBAT_INFO.defender[i] = createHeroObj(VEDIO_COMBAT_INFO, atkConf[1], atkConf[3], atkConf[4], i)
        end
    end

    combatVedio(VEDIO_COMBAT_INFO, VEDIO_COMBAT_INFO.atkFormation, VEDIO_COMBAT_INFO.attacker, 
    VEDIO_COMBAT_INFO.defFormation, VEDIO_COMBAT_INFO.defender, nil, nil, human)
end


function repeatCombat(human, combatInfo)
	if not combatInfo then
		return
	end

	if isCombating(human) then
		return Broadcast.sendErr(human, Lang.COMBAT_VIDEO_LOOK_ERR_ING)
	end
	
	combatInfo.isVideo = true
	human.combat = combatInfo	
	sendCombatData(human, combatInfo)
    sendCombatFinish(human, combatInfo)
end

-- 初始属性数据写日志
function makeAttrLogStr(human)
	if not Config.IS_DEBUG then
		return
	end
	local logStr = "\n初始属性数据：\n"
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		data = CombatImpl.objList[i]
		if data then
			logStr = logStr .. "\n\t" .. "{pos:"..data.pos.." heroID:"..data.id.."}\n\t"
			for k,v in pairs(data.attr) do
				if v ~= 0 then
					logStr = logStr .."{key:"..k..",value:"..v.."},"
				end
			end
		end
	end
	logStr = logStr .."\n"
	local f = io.open("./log/fight_log.log", "w+")
	f:write(logStr)
	assert(io.close(f))  
end

function makeLogStr(human)
	if not Config.IS_DEBUG then
		return
	end
	local logStr = "\n战斗开始数据：\n"
	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
		data = CombatImpl.objList[i]
		if data then
			logStr = logStr .. "\t" .. "{pos:"..data.pos.." heroID:"..data.id.."}"
		end
	end
	logStr = logStr .. "\n"
	logStr = logStr .. "战斗帧数据：\n"
	local result = CombatImpl.result
	local frameCnt = result.frames and #result.frames or 0
	for i = 1,frameCnt do
		local data = result.frames[i]
		logStr = logStr .. "\t" .. i .."帧\n"
		for j = 1,#data.skillList do
			local skillData = data.skillList[j]
			logStr = logStr .. "\t\t技能释放  attackPos:"..skillData.attackPos.." skillID:"..skillData.skillID.."\n"
			local hitLen = 0
			for k = 1,CombatDefine.COMBAT_HERO_ALL_CNT do
				if skillData.hitList[k] then
					hitLen = hitLen + 1
					local skillHitData = skillData.hitList[k]
					logStr = logStr .. "\t\t\t技能对象"..hitLen.."  pos:"..k.." hpNow:"..skillHitData.hpNow.." attrs:"
					local frameAttrs = skillHitData.attrs
					local attrsLen = 0
					if frameAttrs then
						for m = 1,#frameAttrs do
							logStr = logStr .. " {key:"..frameAttrs[m][1].." value:"..frameAttrs[m][2].."}"
						end
					end
					logStr = logStr .. "\n"
				end
			end
			local extraLen = 0
			for k = 1,CombatDefine.COMBAT_HERO_ALL_CNT do
				if skillData.extraList[k] then
					extraLen = extraLen + 1
					local extraHitData = skillData.extraList[k] 
					logStr = logStr .. "\t\t\t额外作用对象"..extraLen.."  pos:"..k.." cmd:"..extraHitData.cmd.." hpNow:"..extraHitData.hpNow.." attrs:"
					local frameAttrs = extraHitData.attrs
					if frameAttrs then
						for m = 1,#frameAttrs do
							logStr = logStr .. " {key:"..frameAttrs[m][1].." value:"..frameAttrs[m][2].."}"
						end
					end
					logStr = logStr .. "\n"
				end
			end
		end
		local bufferLen = 0
		for j = 1,CombatDefine.COMBAT_HERO_ALL_CNT do
			if data.bufferList[j] then
				bufferLen = bufferLen + 1
				logStr = logStr .. "\t\tbuffer更新  pos:"..j.."\n"
				local bufferDataList = data.bufferList[j]
				for i = 1,#bufferDataList do
					local bufferData = bufferDataList[i]
					logStr = logStr .. "\t\t\t bufferID:"..bufferData.id.." op:"..bufferData.op.." cnt:"..bufferData.cnt.." round:"..(bufferData.round or -1).." hpNow:"..bufferData.hpNow.." attrs:"
					local attrsLen = 0
					if bufferData.attrs then
						for k,v in ipairs(bufferData.attrs) do
							logStr = logStr .. " {key:"..v[1].." value:"..v[2].."}"
						end
					end
				end
				logStr = logStr .. "\n"
			end
		end
	end
	local f = io.open("./log/fight_log.log", "a+")
	f:write(logStr)
	assert(io.close(f))  
end

-- 获取角色上阵的形象
function getRoleBody(human, combatType)
	local teamType = CombatPosLogic.getTeamType(combatType)
    local combatHero = teamType and human.db.combatHero[teamType]
    if not combatHero then return end

    local zhandouli = 0
    local maxGrid = nil
    for i = 1, CombatDefine.COMBAT_HERO_CNT do
        local uuid = combatHero.list[i]
        local heroGrid = HeroLogic.getHeroGridByUuid(human, uuid)
        if heroGrid and type(heroGrid) == "table" and 
            zhandouli < heroGrid.zhandouli then
            zhandouli = heroGrid.zhandouli
            maxGrid = heroGrid
        end
    end
    if not maxGrid then return end   
    return HeroGrid.getHeroBodyById(maxGrid.id) 
end

-- 根据怪物组id获取外显
function getMonsterIDByOutID(monsterOutID, args)
    local mocf = MonsterExcel.monsterOut[monsterOutID]
    if not mocf then return end

    local memberCnt = #mocf.member
    local index = (monsterOutID % 1000) % memberCnt
    if index == 0 then index = memberCnt - 1 end

    local minfo = mocf.member[index] 
    if not minfo then
    	for _, minfo0 in ipairs(mocf.member) do
    		if minfo0[1] > 0 then
    			minfo = minfo0
    			break
    		end
    	end
    end
    if not minfo then return end
    local monsterID = minfo[1]
    local monsterLv = minfo[2]
    local mcf = monsterID and MonsterExcel.monster[monsterID]
    if not mcf then return end
    
    if mocf.combatType == CombatDefine.COMBAT_TYPE5 then
		if args then
			local throneID = args[1]
			local evolveCnt = args[2]
			monsterLv = TheStarsLogic.getMonsterLvAttr(1, throneID, evolveCnt)
		end
	end
    
    return monsterID, mcf, monsterLv
end


-- 根据伤害计算胜负
function calcWinByHurt(combatInfo)
    if not combatInfo.isWin then return end
    if not combatInfo.objList then return end

    local atkHurt = 0
    local defHurt = 0
	local atkLive = 0 
	local defLive = 0 
    for i = 1,CombatDefine.COMBAT_HERO_CNT do
       local atkPos = getPos(CombatDefine.ATTACK_SIDE, i)
       local atkObj = combatInfo.objList[atkPos]
       if atkObj then
          atkHurt = atkHurt + atkObj.result[1]
		  if atkObj.hp > 0 and not atkLive then
			 atkLive = 1
		  end
       end

       local defPos = getPos(CombatDefine.DEFEND_SIDE, i)
       local defObj = combatInfo.objList[defPos]
       if defObj then
          defHurt = defHurt + defObj.result[1]
		  if atkObj.hp > 0 and not defLive then
			defLive = 1
		 end
       end
    end

	if atkLive == 0 then
		atkHurt = 0
	elseif defLive == 0 then
		defLive = 0
	end

    combatInfo.isWin = atkHurt > defHurt and true or false
	return combatInfo.isWin
end

------------- reyes test ---------------------
function isCombating(human)
	local list = COMBAT_CACHE[human.db._id]
	if not list then return end

	for combatType, combatCache in pairs(list) do
		if not combatCache.combatInfo.isVideo then
			return true
		end
	end
end

function getCombatCache(uuid, type)
	return COMBAT_CACHE[uuid] and COMBAT_CACHE[uuid][type] 
end

function addCombatCache(uuid, combatInfo, cbParam,param)
	COMBAT_CACHE[uuid] = COMBAT_CACHE[uuid] or {} -- reyes test
	local tempCache = {}
	local result = combatInfo.isWin and CombatDefine.RESULT_WIN or CombatDefine.RESULT_FAIL	
	tempCache.result = result
	tempCache.combatType = combatInfo.type
	tempCache.cbParam = cbParam
	tempCache.combatInfo = combatInfo
	tempCache.param = param
	tempCache.ts = os.time() + getCombatUseTime(combatInfo)
	COMBAT_CACHE[uuid][combatInfo.type] = tempCache
end

function cleanCombatCache(uuid, type)
	if not COMBAT_CACHE[uuid] then return end
	COMBAT_CACHE[uuid][type] = nil

	if Util.getTableCount(COMBAT_CACHE[uuid]) == 0 then
		COMBAT_CACHE[uuid] = nil
	end
end

function clientFinish(human, type, noSend)
	if COMBAT_CACHE[human.db._id] == nil or COMBAT_CACHE[human.db._id][type] == nil then
		return
	end
	
	local combatCache = COMBAT_CACHE[human.db._id][type]
	cleanCombatCache(human.db._id, type)

	--print("onend-=----------------------")
	if not combatCache.combatInfo.isVideo then
		onFightEnd(human, combatCache.result, combatCache.combatType, combatCache.cbParam, combatCache.combatInfo,combatCache.param)	
	end

	-- 绝望深渊 非失败 不发
    -- 冰龙 次元魔珠 失落神庙 不走默认结算协议
	if type ~= CombatDefine.COMBAT_TYPE8 and
       type ~= CombatDefine.COMABT_TYPE23 and
       type ~= CombatDefine.COMBAT_TYPE20 and
       type ~= CombatDefine.COMBAT_TYPE24
        and not noSend then
		sendCombatFinish(human, combatCache.combatInfo)
	end
    
end


function clientPassTime(human, type, passTime)
	if COMBAT_CACHE[human.db._id] == nil or COMBAT_CACHE[human.db._id][type] == nil then
		return
	end
	
    local combatInfo = COMBAT_CACHE[human.db._id][type].combatInfo
    combatInfo.passTime = passTime
end

function onLogin(human)
	checkFinish(human)

	-- 这里要先check才判断cache 因为check完cache可能就空了 
	if COMBAT_CACHE[human.db._id] == nil then
		return
	end
	
	for k, v in pairs(COMBAT_CACHE[human.db._id]) do
		--assert(nil, "has combat cache "..combatType .. " " .. human.db.account) -- reyes test
		human.combat = v.combatInfo	
		sendCombatData(human, human.combat, true)
	end
end


function checkFinish(human)
	if COMBAT_CACHE[human.db._id] == nil then
		return
	end

	local now = os.time()
	for k, v in pairs(COMBAT_CACHE[human.db._id]) do
		if now >= v.ts then
			clientFinish(human, k)
			if COMBAT_CACHE[human.db._id] == nil then
				return
			end
		end
	end
end

-- 不管时间立刻结算 目前只有踢人等非正常下线会用到
function forceFinish(human) 
	if COMBAT_CACHE[human.db._id] == nil then
		return
	end

	local now = os.time()
	for k, v in pairs(COMBAT_CACHE[human.db._id]) do
		clientFinish(human, k)
		if COMBAT_CACHE[human.db._id] == nil then
			return
		end
	end
end


function killFrames(combatInfo)
	--60帧2倍速情况下大概1分半。限制录像时长再3-5分钟。节省包量
	if combatInfo.result.frames and  #combatInfo.result.frames > 200 then
        for i = 201,#combatInfo.result.frames do
            combatInfo.result.frames[i] = nil
        end
    end
end